
#include <ddk.h>

#include "uffs/uffs_config.h"
#include "uffs/uffs_utils.h"
#include "uffs\uffs_os.h"

#include "CUffsFileSystem.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)

#include "_CUffsFileSystem.cpp"

#include <stdlib.h>
#include "uffs\uffs.h"
#include "uffs\uffs_fs.h"

#include "uffserrors.h"
#include "CUffsFile.h"
#include "CUffsDirectory.h"

extern "C" void femu_init_uffs_device(uffs_Device *dev);

#define MAX_MOUNT_POINT_NAME 32

CUffsFileSystem::CUffsFileSystem()
{
    m_uffsRootDir = NULL;
    memset(&m_conf_mounts, 0, sizeof(uffs_mountTable));
    memset(&m_conf_devices, 0, sizeof(uffs_Device));
    memset(m_mount_point_name, 0, MAX_MOUNT_POINT_NAME);

    m_conf_mounts.dev = &(m_conf_devices);
	m_conf_mounts.mountPoint = "/";
	m_conf_mounts.startBlock = 0;
	m_conf_mounts.endBlock = -1;
}

CUffsFileSystem::~CUffsFileSystem()
{
}

ECode CUffsFileSystem::Sync()
{
    m_uffsRootDir->Sync();
    return NOERROR;
}

ECode CUffsFileSystem::GetRoot(
    /* [out] */ IBasicDirectory **ppRoot)
{
    if (ppRoot == NULL) {
        return E_INVALID_ARGUMENT;
    }

    if (NULL == m_uffsRootDir) {
        return E_FS_NO_ROOT;
    }

    m_uffsRootDir->AddRef();
    *ppRoot = m_uffsRootDir;
    return NOERROR;
}

ECode CUffsFileSystem::Lookup(
    /* [in] */ WString path,
    /* [out] */ IBasicFile ** ppFile)
{
    if (m_uffsRootDir == NULL) {
        return E_FS_NO_ROOT;
    }

    return m_uffsRootDir->Lookup(path, ppFile);
}

ECode CUffsFileSystem::GetStat(
    /* [out] */ FileSystemStat * pFileSystemStat)
{
    // easy to do, but i need to change the definite of FileSystemStat
    // because of the deference between fat dist and nand flash.
//    kprintf("GetStat-------->in\n");
    if (NULL == pFileSystemStat) {
        return E_INVALID_ARGUMENT;
    }

    pFileSystemStat->nClusterSize = (m_conf_mounts.dev->attr.page_data_size) * (m_conf_mounts.dev->attr.pages_per_block);
    pFileSystemStat->nTotalClusters = m_conf_mounts.dev->attr.total_blocks;
    pFileSystemStat->nFreeClusters = m_conf_mounts.dev->tree.erasedCount-2;
//todo ..............
    pFileSystemStat->fsType = FatType_FAT16;

//    kprintf("GetStat-------->out\n");
    return NOERROR;
}

/* The driver name must be the mount point name */
ECode CUffsFileSystem::BindDriver(
    /* [in] */ WString wstrDevName)
{
    uffs_Object *obj = NULL;
    uffs_Device *dev = NULL;
    CUffsDirectory *pUfsDir = NULL;
	URET ret;
	ECode ec;
	struct uffs_mountTableSt *mtbl = NULL;
    char szName[MAX_MOUNT_POINT_NAME] = {0};

    kprintf("BindDriver: %S\n", (const wchar_t*)wstrDevName);
    if (wstrDevName.IsNullOrEmpty()) {
        return E_INVALID_ARGUMENT;
    }

    if (NULL != m_uffsRootDir) {
        kprintf("m_uffsRootDir is not NULL, maybe initialize twice\n");
        return -1;
    }

    uffs_InitCS();
    femu_init_uffs_device(m_conf_mounts.dev);
    mtbl = &(m_conf_mounts);

    //while(mtbl->dev) {
		uffs_RegisterMountTable(mtbl);
		//mtbl++;
	//}

	ret = uffs_initMountTable();
	if (ret != U_SUCC) {
	    kprintf("uffs_initMountTable failed\n");
	    ec = -1;
	    goto Exit;
	}

    WcsToMbs(szName, wstrDevName, MAX_MOUNT_POINT_NAME);

    if ((dev = uffs_GetDevice(szName)) == NULL ) {
        kprintf("can't mount:%s\n", szName);
        ec = -1;
        goto Exit;
    }

    kprintf("mount:%s.Success\n", szName);
    uffs_PutDevice(dev);

    obj = uffs_GetObject();
    if (obj == NULL) {
        kprintf("fail to get obj\n");
        ec = -1;
        goto Exit;
    }

    pUfsDir = new CUffsDirectory();
    if (pUfsDir == NULL) {
        ec = E_OUT_OF_MEMORY;
        goto Exit;
    }

    obj->handle = (u32)pUfsDir;
    ret = uffs_OpenObjectRoot(obj, szName, UO_RDWR|UO_DIR, US_WRITE);
    if (ret != U_SUCC) {
        ec = E_UFFS_OPEN_FAILED;
        kprintf("uffs_OpenObjectRootFailed\n");
        goto Exit;
    } else {
        //pUffsDir
        pUfsDir->Initialize(obj);
        pUfsDir->AddRef();
        m_uffsRootDir = pUfsDir;
    }

    uffs_PutObject(obj);

    return NOERROR;

Exit:
    if (pUfsDir) {
        delete pUfsDir;
    }

    uffs_releaseMountTable();

    uffs_UninitCS();

    return ec;
}

ECode CUffsFileSystem::UnbindDriver()
{
    kprintf("UnbindDriver\n");
    if (m_uffsRootDir != NULL) {
        m_uffsRootDir->Release();
        m_uffsRootDir = NULL;
    }

    uffs_releaseMountTable();

    uffs_UninitCS();

    return NOERROR;
}

void CUffsFileSystem::SetStub(IStub *pStub)
{
    m_pStub = pStub;
}

IStub *CUffsFileSystem::GetStub()
{
    return m_pStub;
}

